#!/usr/bin/env node
// 自动化构建脚本
// 用于处理不同环境和平台的构建、打包和部署流程

const fs = require('fs');
const path = require('path');
const { execSync, exec } = require('child_process');
const chalk = require('chalk');
const ora = require('ora');
const inquirer = require('inquirer');
const rimraf = require('rimraf');
const archiver = require('archiver');
const { getConfig } = require('../config/deploy.config.js');

// 常量定义
const ROOT_DIR = path.resolve(__dirname, '..');
const DIST_DIR = path.join(ROOT_DIR, 'dist');
const BACKUP_DIR = path.join(ROOT_DIR, 'backups');
const PACKAGE_FILE = path.join(ROOT_DIR, 'package.json');
const CHANGELOG_FILE = path.join(ROOT_DIR, 'CHANGELOG.md');

// 颜色输出函数
const log = {
  info: (msg) => console.log(chalk.blue(`[INFO] ${msg}`)),
  success: (msg) => console.log(chalk.green(`[SUCCESS] ${msg}`)),
  warning: (msg) => console.log(chalk.yellow(`[WARNING] ${msg}`)),
  error: (msg) => console.log(chalk.red(`[ERROR] ${msg}`)),
  debug: (msg) => console.log(chalk.gray(`[DEBUG] ${msg}`))
};

// 执行命令函数
function runCommand(command, options = {}) {
  const { silent = false, cwd = ROOT_DIR } = options;
  
  log.debug(`Running command: ${command}`);
  if (!silent) {
    log.info(`执行: ${command}`);
  }
  
  try {
    return execSync(command, {
      stdio: silent ? 'pipe' : 'inherit',
      cwd,
      shell: true
    }).toString().trim();
  } catch (error) {
    log.error(`Command failed: ${command}`);
    log.error(error.stderr?.toString() || error.message);
    throw error;
  }
}

// 异步执行命令函数
function runCommandAsync(command, options = {}) {
  return new Promise((resolve, reject) => {
    const { silent = false, cwd = ROOT_DIR } = options;
    
    log.debug(`Running async command: ${command}`);
    if (!silent) {
      log.info(`异步执行: ${command}`);
    }
    
    const child = exec(command, {
      cwd,
      shell: true
    });
    
    let stdout = '';
    let stderr = '';
    
    if (!silent) {
      child.stdout.pipe(process.stdout);
      child.stderr.pipe(process.stderr);
    } else {
      child.stdout.on('data', data => {
        stdout += data;
      });
      
      child.stderr.on('data', data => {
        stderr += data;
      });
    }
    
    child.on('close', code => {
      if (code === 0) {
        resolve(stdout);
      } else {
        log.error(`Async command failed: ${command}`);
        log.error(stderr);
        reject(new Error(`Command failed with exit code ${code}`));
      }
    });
  });
}

// 清理目录函数
function cleanDirectory(dirPath) {
  log.info(`清理目录: ${dirPath}`);
  if (fs.existsSync(dirPath)) {
    rimraf.sync(dirPath);
  }
  fs.mkdirSync(dirPath, { recursive: true });
}

// 创建备份函数
async function createBackup(env, platform, version) {
  const spinner = ora('创建构建备份').start();
  
  try {
    // 确保备份目录存在
    if (!fs.existsSync(BACKUP_DIR)) {
      fs.mkdirSync(BACKUP_DIR, { recursive: true });
    }
    
    const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
    const backupName = `backup-${platform}-${env}-${version}-${timestamp}`;
    const backupFile = path.join(BACKUP_DIR, `${backupName}.zip`);
    const output = fs.createWriteStream(backupFile);
    const archive = archiver('zip', {
      zlib: { level: 9 }
    });
    
    output.on('close', () => {
      spinner.succeed(`备份创建成功: ${backupFile} (${(archive.pointer() / 1024 / 1024).toFixed(2)}MB)`);
      log.success(`备份文件已保存: ${backupFile}`);
    });
    
    archive.on('warning', (err) => {
      log.warning(`备份警告: ${err.message}`);
    });
    
    archive.on('error', (err) => {
      spinner.fail('备份创建失败');
      throw err;
    });
    
    archive.pipe(output);
    
    // 添加构建文件到备份
    if (fs.existsSync(DIST_DIR)) {
      archive.directory(DIST_DIR, false);
    }
    
    // 添加配置文件
    archive.file(path.join(ROOT_DIR, 'config', 'deploy.config.js'), { name: 'config/deploy.config.js' });
    
    await archive.finalize();
    
    // 清理旧备份，保留最新的10个
    const backups = fs.readdirSync(BACKUP_DIR)
      .filter(file => file.startsWith('backup-') && file.endsWith('.zip'))
      .map(file => ({
        name: file,
        mtime: fs.statSync(path.join(BACKUP_DIR, file)).mtime
      }))
      .sort((a, b) => b.mtime - a.mtime);
    
    while (backups.length > 10) {
      const oldest = backups.pop();
      const filePath = path.join(BACKUP_DIR, oldest.name);
      fs.unlinkSync(filePath);
      log.debug(`删除旧备份: ${oldest.name}`);
    }
    
    return backupFile;
  } catch (error) {
    spinner.fail('备份创建失败');
    log.error(`备份创建错误: ${error.message}`);
    throw error;
  }
}

// 执行预构建检查
async function preBuildChecks() {
  const spinner = ora('执行预构建检查').start();
  
  try {
    // 检查Node.js版本
    const nodeVersion = runCommand('node -v', { silent: true });
    log.debug(`Node.js 版本: ${nodeVersion}`);
    
    // 检查npm依赖
    log.info('检查依赖项...');
    if (!fs.existsSync(path.join(ROOT_DIR, 'node_modules'))) {
      log.warning('node_modules 目录不存在，正在安装依赖...');
      runCommand('npm install', { silent: false });
    }
    
    // 执行代码检查
    log.info('执行代码检查...');
    runCommand('npm run lint', { silent: false });
    
    // 运行单元测试
    log.info('运行单元测试...');
    runCommand('npm run test:unit', { silent: false });
    
    spinner.succeed('预构建检查通过');
    return true;
  } catch (error) {
    spinner.fail('预构建检查失败');
    log.error(`预构建检查错误: ${error.message}`);
    throw error;
  }
}

// 获取版本信息
function getVersionInfo() {
  const packageJson = JSON.parse(fs.readFileSync(PACKAGE_FILE, 'utf8'));
  return packageJson.version;
}

// 更新版本号
function updateVersion(type = 'patch') {
  const packageJson = JSON.parse(fs.readFileSync(PACKAGE_FILE, 'utf8'));
  const currentVersion = packageJson.version;
  const versionParts = currentVersion.split('.').map(Number);
  
  switch (type) {
    case 'major':
      versionParts[0]++;
      versionParts[1] = 0;
      versionParts[2] = 0;
      break;
    case 'minor':
      versionParts[1]++;
      versionParts[2] = 0;
      break;
    case 'patch':
    default:
      versionParts[2]++;
      break;
  }
  
  const newVersion = versionParts.join('.');
  packageJson.version = newVersion;
  
  fs.writeFileSync(PACKAGE_FILE, JSON.stringify(packageJson, null, 2));
  log.success(`版本号已更新: ${currentVersion} -> ${newVersion}`);
  
  return newVersion;
}

// 生成构建命令
function generateBuildCommand(env, platform) {
  const commandMap = {
    h5: `npm run build:h5`,
    app: `npm run build:app`,
    mp_weixin: `npm run build:mp-weixin`,
    mp_alipay: `npm run build:mp-alipay`,
    mp_baidu: `npm run build:mp-baidu`,
    mp_toutiao: `npm run build:mp-toutiao`,
    mp_qq: `npm run build:mp-qq`
  };
  
  let command = commandMap[platform] || `npm run build:${platform}`;
  
  // 设置环境变量
  command = `cross-env NODE_ENV=${env} UNI_PLATFORM=${platform} ${command}`;
  
  return command;
}

// 执行构建
async function buildProject(env, platform, version) {
  const spinner = ora(`构建 ${platform} 平台 (${env} 环境)`).start();
  
  try {
    // 清理构建目录
    cleanDirectory(DIST_DIR);
    
    // 生成构建命令
    const buildCommand = generateBuildCommand(env, platform);
    log.info(`开始构建: ${platform} (${env})`);
    log.debug(`构建命令: ${buildCommand}`);
    
    // 执行构建
    await runCommandAsync(buildCommand);
    
    spinner.succeed(`构建完成: ${platform} (${env})`);
    
    // 构建产物统计
    const distStats = fs.statSync(DIST_DIR);
    log.success(`构建产物目录: ${DIST_DIR}`);
    
    // 统计文件数量
    let fileCount = 0;
    function countFiles(dir) {
      const files = fs.readdirSync(dir);
      files.forEach(file => {
        const filePath = path.join(dir, file);
        const stats = fs.statSync(filePath);
        if (stats.isDirectory()) {
          countFiles(filePath);
        } else {
          fileCount++;
        }
      });
    }
    countFiles(DIST_DIR);
    log.info(`构建产物包含 ${fileCount} 个文件`);
    
    return true;
  } catch (error) {
    spinner.fail(`构建失败: ${platform} (${env})`);
    log.error(`构建错误: ${error.message}`);
    throw error;
  }
}

// 打包构建产物
function packageBuild(platform, version) {
  const spinner = ora(`打包构建产物`).start();
  
  try {
    const packageDir = path.join(ROOT_DIR, 'packages');
    if (!fs.existsSync(packageDir)) {
      fs.mkdirSync(packageDir, { recursive: true });
    }
    
    const packageName = `${platform}-${version}.zip`;
    const packagePath = path.join(packageDir, packageName);
    const output = fs.createWriteStream(packagePath);
    const archive = archiver('zip', {
      zlib: { level: 9 }
    });
    
    output.on('close', () => {
      spinner.succeed(`打包完成: ${packageName} (${(archive.pointer() / 1024 / 1024).toFixed(2)}MB)`);
      log.success(`构建包已保存: ${packagePath}`);
    });
    
    archive.on('warning', (err) => {
      log.warning(`打包警告: ${err.message}`);
    });
    
    archive.on('error', (err) => {
      spinner.fail('打包失败');
      throw err;
    });
    
    archive.pipe(output);
    archive.directory(DIST_DIR, false);
    archive.finalize();
    
    return packagePath;
  } catch (error) {
    spinner.fail('打包失败');
    log.error(`打包错误: ${error.message}`);
    throw error;
  }
}

// 更新构建信息文件
function updateBuildInfo(env, platform, version) {
  const buildInfo = {
    projectName: 'StarMate',
    version,
    platform,
    environment: env,
    buildTime: new Date().toISOString(),
    gitCommit: runCommand('git rev-parse --short HEAD', { silent: true }) || 'unknown',
    nodeVersion: runCommand('node -v', { silent: true }),
    npmVersion: runCommand('npm -v', { silent: true })
  };
  
  const buildInfoDir = path.join(DIST_DIR, 'static');
  if (!fs.existsSync(buildInfoDir)) {
    fs.mkdirSync(buildInfoDir, { recursive: true });
  }
  
  fs.writeFileSync(
    path.join(buildInfoDir, 'build-info.json'),
    JSON.stringify(buildInfo, null, 2)
  );
  
  log.info(`构建信息已更新: ${JSON.stringify(buildInfo)}`);
  
  return buildInfo;
}

// 更新部署记录
function updateDeployLog(env, platform, version, success) {
  const logEntry = {
    timestamp: new Date().toISOString(),
    environment: env,
    platform,
    version,
    success,
    user: process.env.USER || process.env.USERNAME || 'unknown',
    description: success ? '部署成功' : '部署失败'
  };
  
  const deployLogDir = path.join(ROOT_DIR, 'logs');
  const deployLogFile = path.join(deployLogDir, 'deploy.log');
  
  if (!fs.existsSync(deployLogDir)) {
    fs.mkdirSync(deployLogDir, { recursive: true });
  }
  
  fs.appendFileSync(
    deployLogFile,
    JSON.stringify(logEntry) + '\n'
  );
  
  log.info(`部署记录已更新: ${deployLogFile}`);
  
  return logEntry;
}

// 执行构建流程
async function runBuild(env, platform, options = {}) {
  const { bumpVersion = false, createBackupFlag = true, packageBuildFlag = true } = options;
  
  try {
    let version = getVersionInfo();
    
    // 更新版本号
    if (bumpVersion) {
      version = updateVersion('patch');
    }
    
    // 执行预构建检查
    await preBuildChecks();
    
    // 执行构建
    await buildProject(env, platform, version);
    
    // 更新构建信息
    updateBuildInfo(env, platform, version);
    
    // 创建备份
    if (createBackupFlag) {
      await createBackup(env, platform, version);
    }
    
    // 打包构建产物
    let packagePath;
    if (packageBuildFlag) {
      packagePath = packageBuild(platform, version);
    }
    
    // 更新部署记录
    updateDeployLog(env, platform, version, true);
    
    log.success(`构建流程完成!`);
    log.success(`环境: ${env}`);
    log.success(`平台: ${platform}`);
    log.success(`版本: ${version}`);
    if (packagePath) {
      log.success(`包路径: ${packagePath}`);
    }
    
    return {
      success: true,
      version,
      packagePath
    };
  } catch (error) {
    // 更新失败的部署记录
    updateDeployLog(env, platform, getVersionInfo(), false);
    log.error(`构建流程失败: ${error.message}`);
    throw error;
  }
}

// 交互式构建配置
async function interactiveConfig() {
  log.info('=== StarMate 构建工具 ===');
  log.info('请选择构建配置:');
  
  const answers = await inquirer.prompt([
    {
      type: 'list',
      name: 'env',
      message: '选择环境:',
      choices: [
        { name: '开发环境', value: 'development' },
        { name: '测试环境', value: 'test' },
        { name: '预发布环境', value: 'preproduction' },
        { name: '生产环境', value: 'production' }
      ],
      default: 'development'
    },
    {
      type: 'list',
      name: 'platform',
      message: '选择平台:',
      choices: [
        { name: 'H5', value: 'h5' },
        { name: 'App', value: 'app' },
        { name: '微信小程序', value: 'mp_weixin' },
        { name: '支付宝小程序', value: 'mp_alipay' },
        { name: '百度小程序', value: 'mp_baidu' },
        { name: '头条小程序', value: 'mp_toutiao' },
        { name: 'QQ小程序', value: 'mp_qq' }
      ],
      default: 'h5'
    },
    {
      type: 'confirm',
      name: 'bumpVersion',
      message: '是否更新版本号?',
      default: false
    },
    {
      type: 'confirm',
      name: 'createBackup',
      message: '是否创建备份?',
      default: true
    },
    {
      type: 'confirm',
      name: 'packageBuild',
      message: '是否打包构建产物?',
      default: true
    }
  ]);
  
  return answers;
}

// 主函数
async function main() {
  try {
    // 解析命令行参数
    const args = process.argv.slice(2);
    
    let env = 'development';
    let platform = 'h5';
    let options = {
      bumpVersion: false,
      createBackupFlag: true,
      packageBuildFlag: true
    };
    
    // 如果没有参数，使用交互式配置
    if (args.length === 0) {
      const config = await interactiveConfig();
      env = config.env;
      platform = config.platform;
      options = {
        bumpVersion: config.bumpVersion,
        createBackupFlag: config.createBackup,
        packageBuildFlag: config.packageBuild
      };
    } else {
      // 简单的参数解析
      for (let i = 0; i < args.length; i++) {
        const arg = args[i];
        switch (arg) {
          case '--env':
          case '-e':
            env = args[++i];
            break;
          case '--platform':
          case '-p':
            platform = args[++i];
            break;
          case '--bump-version':
          case '-v':
            options.bumpVersion = true;
            break;
          case '--no-backup':
            options.createBackupFlag = false;
            break;
          case '--no-package':
            options.packageBuildFlag = false;
            break;
          case '--help':
          case '-h':
            console.log('使用方法:');
            console.log('  node scripts/build.js [options]');
            console.log('');
            console.log('选项:');
            console.log('  --env, -e <environment>    指定环境 (development, test, preproduction, production)');
            console.log('  --platform, -p <platform>  指定平台 (h5, app, mp_weixin, mp_alipay, etc.)');
            console.log('  --bump-version, -v         更新版本号');
            console.log('  --no-backup                不创建备份');
            console.log('  --no-package               不打包构建产物');
            console.log('  --help, -h                 显示帮助信息');
            process.exit(0);
        }
      }
    }
    
    // 执行构建
    await runBuild(env, platform, options);
    
    process.exit(0);
  } catch (error) {
    log.error(`构建过程中发生错误: ${error.message}`);
    process.exit(1);
  }
}

// 执行主函数
if (require.main === module) {
  main();
}

// 导出模块
exports.runBuild = runBuild;
exports.generateBuildCommand = generateBuildCommand;
exports.preBuildChecks = preBuildChecks;
exports.cleanDirectory = cleanDirectory;